<?php
/*

	‘орм-процессор
	* see http://in.jetstyle.ru/rocket/rocketforms

	FormComponent_interface_string( &$config )
			- $field -- $field->config instance-a пол€

	-------------------

	* interface   : —“–ќ ј. типовой интерфейс строки

	-------------------

	// »нтерфейс (парсинг и обработка данных)

	* Interface_SafeDataValue( $data_value )
	* Interface_Parse()
	* Interface_PostToArray( $post_data )

================================================================== v.0 (kuso@npj)
 */

class FormComponent_filter_tinymce extends FormComponent_abstract
{
	// default tinymce rules
#img[src=|alt=|title],
#<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="350" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0">
#<param name="src" value="http://www.youtube.com/v/illnkdHT3lM" /><embed type="application/x-shockwave-flash" width="425" height="350" src="http://www.youtube.com/v/illnkdHT3lM"></embed>
#</object>

	// @see http://wiki.moxiecode.com/index.php/TinyMCE:Configuration/valid_elements
	public $valid_elements = "
		+a[rel|rev|name|href=|target|title],
		-strong/-b[],
		-em[]/-i,
		-strike[],
		-u[],
		-p[],
		-ol[],
		-ul[],
		-li[],
		br,
		object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],
		img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|obj|param|embed]
		-sub[],
		-sup[],
		-blockquote[],
		-table[class],
		-tr[rowspan|align|valign],
		tbody[],
		thead[],
		tfoot[],
		-td[class|colspan|rowspan|align|valign],
		-th[class|colspan|rowspan|align|valign],
		caption[],
		-div[class],
		-span[class],
		-pre[],
		address[],
		-h1[],
		-h2[],
		-h3[],
		-h4[],
		-h5[],
		-h6[],
		hr[],
		dd[],
		dl[],
		dt[],
		";

	public function filtrate ()
	{
		$value = $this->field->model->Model_GetDataValue();
		if (isset($this->field->config['filter_params'])) {
			$args = $this->field->config['filter_params'];
			//foreach ($args as $k=>$v) { $this->$k = $v; }
		}
		//var_dump($value);
		$rules = tinymce_parse_elements_rules($this->valid_elements);
		$value = $this->process_tinymce($value, $rules);
		//var_dump($value); die();
		$this->field->model->Model_SetDataValue($value);
	} // end of method filter

	public function process_tinymce ($html, $rules)
	{
		// specify charset encoding
		$html = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'.$html;

		// preserve HTML entyties
		$html = strtr($html, array('&' => '&amp;'));

		// create DOM
		$doc = new DOMDocument();
		//$doc->encoding = 'UTF-8';
		//$doc->preserveWhiteSpace = true;
		//$doc->substituteEntities = true;
		//$doc->strictErrorChecking = false;
		$doc->loadHTML($html);

		// process DOM and return result
		return tinymce_serializeNodeToHtml($doc->documentElement, $rules);
	} // end of method process_tinymce

}

function html_serialize_attr ($name, $value)
{
	if ($name && isset($value)) {
		return $name . '="' . $value . '"';
	}
	return '';
} // end of function html_make_attr
function html_serialize_text ($value)
{
	return $value;
} // end of function html_make_text
function html_serialize_comment ($value)
{
	return $value;
} // end of function html_serialize_comment


global $_TINIMCE_COMPILED_RULES;
if (!isset($_TINIMCE_COMPILED_RULES)) $_TINIMCE_COMPILED_RULES = array();
function tinymce_parse_elements_rules ($str)
{
	global $_TINIMCE_COMPILED_RULES; // compiled cache
	if (array_key_exists($str, $_TINIMCE_COMPILED_RULES)) { // if cached
		$rules_map = $_TINIMCE_COMPILED_RULES[$str];
	} else {
		// compile rules
		$rules = tinimce__parse_elements_rules($str);

		// convert rules
		$rules_map = array();
		foreach ($rules as $rs) {
			foreach ($rs as $rule) {
				if ($rule) { // if not empty
					$tagName = $rule['tagName'];
					$rule['oTagName'] = $rs[0]['tagName'];
					$rules_map[$tagName] = $rule;
				}
			}
		}

		// cache rules
		$_TINIMCE_COMPILED_RULES[$str] = $rules_map;
	}
	return $rules_map;
} // end of function tinymce_parse_elements_rules
function tinymce_serializeNodeToHtml ($n, $rules)
{
	//return  $n->ownerDocument->saveHtml();
	$html = '';
	switch ($n->nodeType) {

	case XML_ELEMENT_NODE: // Element
		$nn = $n->tagName;
		$tag_open = $tag_close = $inner = '';
		$serializeNode = true;

		// process childs
		if ($n->hasChildNodes()) {
			foreach ($n->childNodes as $child) {
				$inner .= tinymce_serializeNodeToHtml($child, $rules);
			}
		}

		// construct tag by rules
		if (isset($rules[$nn])) {
			$rule = $rules[$nn];
			if ($rule['removeEmpty'] && empty($inner)) { // remove node if empty
			} else { // output node
				$tag_open = '<'.$rule['oTagName'];
				// construct attrs
				if (isset($rule['attrs'])) { // accept attrs, specified by rule only
					foreach ($rule['attrs'] as $attr) {
						if ($n->hasAttribute($attr['name'])) {
							if (isset($attr['forcedValue'])) {
								$tag_open .= ' '.html_serialize_attr($attr['name'], $attr['forcedValue']);
							} else {
								$tag_open .= ' '.html_serialize_attr($attr['name'], $n->getAttribute($attr['name']));
							}
						} elseif (isset($attr['default'])) {
							$tag_open .= ' '.html_serialize_attr($attr['name'], $attr['default']);
						} elseif ($attr['required']) { // attr required but not exists
							// remove tag
							$serializeNode = false;
						}
					}
				} else { // accept all attrs
					if ($n->hasAttributes()) {
						// just copy attrs
						foreach ($n->attributes as $name=>$value) {
							$tag_open .= ' '.html_serialize_attr($name, $value);
						}
					}
				}
				if (empty($inner) && $rule['padEmpty']) {
					$inner = '&nbsp;';
				}
				if (empty($inner) && !$rule['keepEmpty']) {
					$tag_open .= '/>';
				} else  {
					$tag_open .= '>';
					$tag_close = '</'.$rule['oTagName'].'>';
				}
			}
		}

		// render output
		if ($serializeNode) $html = $tag_open . $inner . $tag_close;
		break;

	case XML_TEXT_NODE:          // Text
	case XML_CDATA_SECTION_NODE: // CDATA
		//$html .= $n->wholeText;
		$html .= html_serialize_text($n->nodeValue);
		//$html .= html_serialize_text($n->textContent);
		break;

	case XML_COMMENT_NODE: // Comment
		$html .= '<!--' . html_serialize_comment($n->nodeValue) . '-->';
		break;
	}
	return $html;
} // end of function tinymce_serializeNodeToHtml


function tinimce__parse_elements_rules ($str)
{
	// match rules
	$rs = preg_split('#\s*,\s*#siu', $str);
	$rules = array();
	foreach ($rs as $r) {
		$r  = trim($r);
		// match tags
		$ts = explode('/', $r);
		$rus = array();
		foreach ($ts as $t) {
			$rule = array();

			// match tag
			if (preg_match('/^([-+#])?([[:alnum:]]+)(?:\\[(.*)\\])?$/siu', $t,
				$ms)) {
					$rule[    'tagName'] = $ms[2];
					$rule['removeEmpty'] = ($ms[1] == '-');
					$rule[  'keepEmpty'] = ($ms[1] == '+');
					$rule[   'padEmpty'] = ($ms[1] == '#');
					$rule[      'attrs'] = isset($ms[3]) ? array() : NULL;

					// match attrs
					if (isset($ms[3]) && $ms[3]) {
						$as = explode('|', $ms[3]);
						foreach ($as as $a) {
							$ar = array(
								       'name' => NULL,
								    'default' => NULL,
								   'required' => false,
								'forcedValue' => NULL,
								);

							// has default value for ?
							$av = explode('=', $a, 2);
							if (count($av) === 2) {
								// match attr=[default]
								$ar[   'name'] = $av[0];
								$ar['default'] = $av[1];
							} else {

								// has forced value for ?
								$av = explode(':', $a, 2);
								if (count($av) === 2) {
									// match attr:forcedValue
									$ar[       'name'] = $av[0];
									$ar['forcedValue'] = $av[1];
								} else {

                  // just attr
									$ar['name'] = $a;
								}
							}

							// is required attr ?
							if (preg_match('#!(.*)$#siu', $ar['name'], $ams)) {
								$ar['name'] = $ams[1];
								$ar['required'] = true;
							}

							// append attr rule
							$rule['attrs'][] = $ar;
						}
					}
					$rule['s'] = $r;
				}
			$rus[] = $rule;
		}
		$rules[] = $rus;
	}
	return $rules;
} // end of function tinimce_parse_rules

